<?php
    
/**
 * Request.php
 *
 * 请求管理类
 * @author wang <wang@fvzone.com>
 * @copyright  Copyright (c) 2010 Fire vision Software Studio
 * @link http://www.fvzone.com
 * @license New BSD License.{@link http://www.opensource.org/licenses/bsd-license.php}
 * @version $Id: Request.php 1.3 2011-12-18 20:30:00Z wang $
 * @package core
 * @since 1.0
 */

defined('IN_WORK') or exit('Authorization error');

class Request extends Work
{
	/**
	 * @var string 当前执行的组件名称，默认为[public]
	 */
	public static $component;

	/**
	 * @var string 当前执行的控制器名称
	 */
	public static $controller;

	/**
	 * @var string 控制器要执行的动作
	 */
	public static $action;

	/**
	 * @var string 控制器要执行的动作
	 */
    public static $get;

	/**
	 * @var string 控制器要执行的动作
	 */
    public static $post;

    /**
     * 应用初始化
     */
    public static function execute()
    {
        //记录初始化时间
        work::time('WORK_INIT_TIME');

        //异常和错误处理
        if (DEBUG == true)
        { 
            //注册默认异常处理函数
            set_exception_handler('Exception_Work::exception_handler');
            //注册默认错误处理函数
            set_error_handler('Exception_Work::error_handler', E_ALL ^ E_NOTICE);
        } else {
            //如果调试关闭，禁止报告所有错误
            error_reporting(0);
        }

        //时区设置,默认为中国(北京时区)
        date_default_timezone_set(TIMEZONE);

        //设置项目系统session的存放目录
        $session_dir = APP_DIR . 'cache/temp';
        if (is_dir($session_dir) && is_writable($session_dir))
            session_save_path($session_dir);

        //关闭魔术变量，提高PHP运行效率
        if (get_magic_quotes_runtime())
            set_magic_quotes_runtime(0);

        //将全局变量进行魔术变量处理，过滤掉系统自动加上的[\]
        if (get_magic_quotes_gpc())
        {
            $_POST    = self::stripSlashes($_POST);
            $_GET     = self::stripSlashes($_GET);
            $_SESSION = self::stripSlashes($_SESSION);
            $_COOKIE  = self::stripSlashes($_COOKIE);
        }

        //组件模式开启
        if (COMPONENT_MODEL == true)
        {
            //执行组件基类初始化方法，加载各模块的初始化类init.php文件中的前方法
            Component::init();
        }
        
        //分派路由
        self::dispatch();
    }

    /**
     * 获取当前运行的 component 名称
     *
     * @example [$component = self::component()]
     * @access public
     * @return string controller名称(字母全部小写)
     */
	public static function component($component = null)
	{
		if ($component === null) {
			return self::$component;
		}
		self::$component = (string) $component;
		return self::$component;
	}

    /**
     * 获取当前运行的controller名称
     *
     * @example [$controller = self::controller()]
     * @access public
     * @return string controller名称(字母全部小写)
     */
	public static function controller($controller = null)
	{
		if ($controller === null) {
			return self::$controller;
		}
		self::$controller = (string) $controller;
		return self::$controller;
	}
	
    /**
     * 获取当前运行的action名称
     *
     * @example [$action = self::action()]
     * @access public
     * @return string action名称(字母全部小写)
     */
	public static function action($action = null)
	{
		if ($action === null) {
			return self::$action;
		}
		self::$action = (string) $action;
		return self::$action;
	}

	/**
	 * 项目运行函数
	 * 
	 * 供项目入口文件(index.php)所调用,用于启动框架程序运行
	 * @access public
	 * @return object
	 */
    public static function dispatch()
    {
        //控制器对象实例化列表
        static $apps = array();

        //获取路由信息
        self::fetchUri();

        $controller = self::$controller;
        $action     = self::$action;

        $app = $controller .'.'. $action;

        if (empty($apps[$app]))
        {
            //把 abc_abc 改成 Abc_Abc
            if (strpos($controller, '_') !== false)
                $controller = str_replace(' ', '_', ucwords(str_replace('_', ' ', $controller)));

            //建立控制器类的反射类
            $class  = new ReflectionClass($controller.'_Controller');

            //实例化此类
            $object = $class->newInstance();

            $class->getMethod('before')->invoke($object);

			//如果方法方法不存在
			if (!$class->hasMethod($action . 'Action'))
			{
                throwException("The method called {$action} in the instance {$controller} object Not Found!");
			}
            //执行控制器中的方法
			$apps[$app] = $class->getMethod($action . 'Action');
            $apps[$app]->invoke($object);

        }
        return $apps[$app];
    }

    /**
     * 分析自定义路由规则
	 * @access private
	 * @return string
     */
    private static function routeRule($uri)
    {
        $router_rule_file = PUBLIC_DIR . 'config/router.php';

        if (!is_file($router_rule_file))
            return $uri;

        //加载router的设置文件
        $router_rule = require_once $router_rule_file;

        //利用正则表达式将自定义的网址替换掉.替换为真实的网址
        if (!is_array($router_rule) && sizeof($router_rule) <= 0)
            return $uri;

        foreach ($router_rule as $key => $value)
        {
            $key = str_replace(array(':any', ':num'), array('.+?', '[0-9]+'), $key);

            if (preg_match('#' . $key . '#', $uri))
            {
                $uri = preg_replace('#' . $key . '#', $value, $uri);
                break;
            }
        }
        return $uri;
    }

	/**
	 * 检查URI获取需要的数据
	 * 
	 * @access private
	 * @return string
	 */
    private static function detectUri()
    {
        if (!empty($_SERVER['PATH_INFO']))
        {
            $uri = $_SERVER['PATH_INFO'];
        }
        else if (!empty($_SERVER['REQUEST_URI']))
        {
            $uri = $_SERVER['REQUEST_URI'];
        }
        
        // 删除子路径
        if (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
            $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));

        // 删除单入口文件
        if (strpos($uri,'/'.SCRIPT_NAME) === 0)
            $uri = substr($uri, strlen('/'.SCRIPT_NAME));

        //去掉伪静态网址后缀
        if (strpos($uri, URL_SUFFIX) !== false)
            $uri = substr($uri, 0, -strlen(URL_SUFFIX));

        //过滤掉问号(?)及其后面的所有字符串
        if (($pos = strpos($uri, '?')) !== false)
        {
            $uri = substr($uri, 0, $pos);
        }

        //清除第一个字符
        return trim($uri,'/');
    }
    
	/**
	 * 分析URI信息
	 * 
	 * @return boolean
	 */
    private static function fetchUri()
    {   
        //获取URI数据
        $uri = self::detectUri();

        //自定义URL路由功能
        $uri = self::routeRule($uri);

        //将处理过后的有效URL进行分析,提取有用数据
        $uris = explode(URL_SPLIT, str_replace('/', URL_SPLIT, $uri));

        //开发模式为true时，将第一个参数作为组件目录
        $_component = (COMPONENT_MODEL == true) ? strtolower(array_shift($uris)) : PUBLIC_COMPONENT;
           
        //分别控制器和方法
        $_controller = strtolower(array_shift($uris));
        $_action = strtolower(array_shift($uris));

        //提取需要的操作
        self::$component  = !empty($_component) ? $_component : (!empty(self::$get['component']) ? self::$get['component'] : self::$component);
        self::$controller = !empty($_controller) ? $_controller : (!empty(self::$get['controller']) ? self::$get['controller'] : self::$controller);
        self::$action     = !empty($_action) ? $_action : (!empty(self::$get['action']) ? self::$get['action'] : self::$action);
        
        //参数
        if (sizeof($uris))
        {
            foreach ($uris as $k => $v)
            {
                if ($k % 2 == 0 && isset($uris[$k+1]))
                    $_GET[$v] = $uris[$k+1];
            }
        }

        //过滤XSS和html特殊标签
        self::$get = self::removeXss($_GET);
        self::$post = self::removeXss($_POST);
    }

    /**
     * XSS 跨站攻击过滤，主要使用htmlspecialchars将html字符串转换为实体
     *
     * @param string $string 过滤的字符串或数组
     * @return mixed
     */
    public static function removeXss($string)
    {
        if (is_string($string)) 
        {
            return htmlspecialchars(trim($string), ENT_COMPAT, 'utf-8');
        } 
        else if (is_array($string))
        {
            foreach ($string as $key => $value)
            {
                $string[$key] = self::removeXss($value);
            }
        }
        return $string;
    }

    /**
     * stripslashes()的同功能操作
     *
     * @param string $string 	所要处理的变量
     * @return mixed			变量
     */
    public static function stripSlashes($string)
    {
        if (is_string($string)) 
        {
            return stripslashes($string);
        } 
        else if (is_array($string))
        {
            foreach ($string as $key => $value)
            {
                $string[$key] = self::stripSlashes($value);
            }
        }
        return $string;
    }
}